package utilImg

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"gitee.com/hilaoyu/go-basic-utils/utilFile"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func SaveBase64ToFile(data string, dir string, name string) (string, error) {
	if "" == dir {
		return "", fmt.Errorf("图片保存位置错误")
	}
	if !utilFile.CheckDir(dir) {
		return "", fmt.Errorf("图片保存目录错误")
	}

	b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, data)
	if !b {
		return "", fmt.Errorf("图片数据错误")
	}
	re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`)
	allData := re.FindAllSubmatch([]byte(data), 2)
	fileType := string(allData[0][1]) //png ，jpeg 后缀获取

	base64Str := re.ReplaceAllString(data, "")

	dataByte, _ := base64.StdEncoding.DecodeString(base64Str)

	if "" == name {
		name = strconv.FormatInt(time.Now().UnixMicro(), 10) + "." + fileType
	}

	path := filepath.Join(dir, name)

	err := ioutil.WriteFile(path, dataByte, 0666)
	if nil != err {
		return "", fmt.Errorf("写入文件错误")

	}

	return path, err
}

// Base64FromLocal reads a local file and returns
// the base64 encoded version.
func Base64FromLocal(fname string) (string, error) {
	var b bytes.Buffer

	fileExists := utilFile.Exists(fname)
	if !fileExists {
		return "", fmt.Errorf("File does not exist\n")
	}

	file, err := os.Open(fname)
	if err != nil {
		return "", fmt.Errorf("Error opening file\n")
	}

	_, err = b.ReadFrom(file)
	if err != nil {
		return "", fmt.Errorf("Error reading file to buffer\n")
	}

	return Base64FromBuffer(b), nil
}

// Base64FromRemote is a better named function that
// presently calls NewImage which will be deprecated.
// Function accepts an RFC compliant URL and returns
// a base64 encoded result.
func Base64FromRemote(url string) string {
	image, mime := get(cleanUrl(url))
	enc := encode(image)

	out := format(enc, mime)
	return out
}

// Base64FromBuffer accepts a buffer and returns a
// base64 encoded string.
func Base64FromBuffer(buf bytes.Buffer) string {
	enc := encode(buf.Bytes())
	mime := http.DetectContentType(buf.Bytes())

	return format(enc, mime)
}

func encode(bin []byte) []byte {
	e64 := base64.StdEncoding

	maxEncLen := e64.EncodedLen(len(bin))
	encBuf := make([]byte, maxEncLen)

	e64.Encode(encBuf, bin)
	return encBuf
}

// Lightweight HTTP Client to fetch the image
// Note: This will also pull webpages. @todo
// It is up to the user to use valid image urls.
func get(url string) ([]byte, string) {
	resp, err := http.Get(url)
	if err != nil {
		log.Fatal("Error getting url.")
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	ct := resp.Header.Get("Content-Type")

	if resp.StatusCode == 200 && len(body) > 512 {
		return body, ct
	}

	return []byte(""), ct
}

// format is an abstraction of the mime switch to create the
// acceptable base64 string needed for browsers.
func format(enc []byte, mime string) string {
	switch mime {
	case "image/gif", "image/jpeg", "image/pjpeg", "image/png", "image/tiff":
		return fmt.Sprintf("data:%s;base64,%s", mime, enc)
	default:
	}

	return fmt.Sprintf("data:image/png;base64,%s", enc)
}

// cleanUrl converts whitespace in urls to %20
func cleanUrl(s string) string {
	return strings.Replace(s, " ", "%20", -1)
}

func SaveImage(p string, src image.Image) error {
	f, err := os.OpenFile(p, os.O_SYNC|os.O_RDWR|os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	defer f.Close()
	ext := filepath.Ext(p)
	if strings.EqualFold(ext, ".jpg") || strings.EqualFold(ext, ".jpeg") {
		err = jpeg.Encode(f, src, &jpeg.Options{Quality: 80})
	} else if strings.EqualFold(ext, ".png") {
		err = png.Encode(f, src)
	} else if strings.EqualFold(ext, ".gif") {
		err = gif.Encode(f, src, &gif.Options{NumColors: 256})
	}
	return err
}
